﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  UcmaPresenceProvider.cs
//    
// Abstract:
//
//  This module uses the Unified Communications Managed API to obtain presence 
//  information for a user.
//
//--

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.OfficeCommunicationsServer.Applications.Common;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Collaboration.Presence;
using Microsoft.Rtc.Signaling;
using Microsoft.Rtc.Internal.Collaboration;

namespace Microsoft.EnhancedReminders.Dispatcher
{
    /// <summary>
    /// Class for obtaining someone's presence using the OCS collaboration API.
    /// </summary>
    public class UcmaPresenceProvider : IPresenceProvider
    {
        private readonly LocalEndpoint _endpoint;
        private Context _context;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="endpoint">Registered endpoint to use for querying presence</param>
        public UcmaPresenceProvider(LocalEndpoint endpoint)
        {
            Debug.Assert(endpoint != null, "endpoint != null");

            _endpoint = endpoint;
        }

        #region IPresenceProvider Members

        /// <summary>
        /// Uses the OCS2007 collaboration API to asynchronously obtain presence information
        /// for a target user.
        /// </summary>
        /// <param name="notification">The NotificationItem this check is being done for</param>
        /// <param name="target">The SIP address for the target user</param>
        public void GetPresenceAsync(NotificationItem notification, string target)
        {
            Debug.Assert(_endpoint != null, "_endpoint != null");
            Debug.Assert(notification != null, "notification != null");
            Debug.Assert(notification.Context != null, "notification.Context != null");

            _context = notification.Context;
            Logger.WriteTraceVerbose(notification.Context, "Beginning presence query.");
            _endpoint.RemotePresence.BeginPresenceQuery(new string[] { target }, new string[] { PresenceState.Name, ContactCard.Name },
                null, QueryPresenceCompleted, new PresenceObtainedEventArgs(notification, target));
        }

        /// <summary>
        /// Called asynchronously when a presence query completes.
        /// </summary>
        /// <param name="asyncResult">The async result.</param>
        private void QueryPresenceCompleted(IAsyncResult asyncResult)
        {
            Debug.Assert(_endpoint != null, "_endpoint != null");
            Debug.Assert(asyncResult != null, "asyncResult != null");

            Logger.WriteTraceVerbose(_context, "Presence query completed. Parsing results.");

            IEnumerable<RemotePresentityNotificationData> results = _endpoint.RemotePresence.EndPresenceQuery(asyncResult);
            PresenceObtainedEventArgs returnResult = asyncResult.AsyncState as PresenceObtainedEventArgs;

            Debug.Assert(results != null, "results != null");
            Debug.Assert(returnResult != null, "returnResult != null");

            foreach (RemotePresentityNotificationData item in results)
            {
                if (item.Categories != null)
                {
                    foreach (PresenceCategoryWithMetaData category in item.Categories)
                    {
                        if (category.Name == PresenceState.Name)
                        {
                            Logger.WriteTraceVerbose(_context, "Parsing presence data");

                            PresenceState state = PresenceState.Create(category);
                            PresenceAvailability availability = PidfDocument.MapToPresenceAvailability((int)state.Availability);

                            returnResult.Presence = ConvertPresenceStateToPresenceStates(availability, state.Activity);
                        }
                        else if (category.Name == ContactCard.Name)
                        {
                            Logger.WriteTraceVerbose(_context, "Parsing contact card data.");

                            GetContactCardInformation(category, returnResult);
                        }
                    }
                }
            }

            EventHandler<PresenceObtainedEventArgs> callback = GetPresenceCompleted;

            if (callback != null)
            {
                callback(this, returnResult);
            }
        }

        private static void GetContactCardInformation(PresenceCategoryWithMetaData category, PresenceObtainedEventArgs results)
        {
            ContactCard card = new ContactCard(category);

            foreach (PhoneNumber number in card.PhoneNumbers)
            {
                if (number.PhoneType == "work")
                {
                    results.DeskPhone = number.Uri;
                }
                else if (number.PhoneType == "mobile")
                {
                    results.MobilePhone = number.Uri;
                }
                else if (number.PhoneType == "home")
                {
                    results.HomePhone = number.Uri;
                }
                else if (number.PhoneType == "other")
                {
                    results.OtherPhone = number.Uri;
                }
            }
        }

        /// <summary>
        /// Parses the UCMA PresenceState object for a specific user and converts it to the
        /// Enhanced Reminders PresenceStates enumeration for comparing to any notification
        /// rules.
        /// </summary>
        private static PresenceStates ConvertPresenceStateToPresenceStates(PresenceAvailability availability, PresenceActivity activity)
        {
            PresenceStates userState = PresenceStates.Unknown;

            switch (availability)
            {
                case PresenceAvailability.Away:
                    userState = PresenceStates.Away;
                    break;
                case PresenceAvailability.BeRightBack:
                    userState = PresenceStates.BeRightBack;
                    break;
                case PresenceAvailability.Busy:
                    // if we find an ActivityToken that we're not aware of, then the
                    // user will be resolved as 'Busy'
                    if (activity == null)
                    {
                        userState = PresenceStates.Busy;
                    }
                    else if (activity.ActivityToken == "on-the-phone")
                    {
                        userState = PresenceStates.InACall;
                    }
                    else if (activity.ActivityToken == "in-a-meeting")
                    {
                        userState = PresenceStates.InAMeeting;
                    }
                    else if (activity.ActivityToken == "in-a-conference")
                    {
                        userState = PresenceStates.InAConference;
                    }
                    else if (activity.ActivityToken == "urgent-interruptions-only")
                    {
                        // This is busy with the string "Urgent Interruptions Only," and is reserved for 
                        // when someone in your team contacts list queries your presence while you're DND.
                        userState = PresenceStates.DoNotDisturb;
                    }
                    else
                    {
                        Logger.WriteTraceWarning("DISPATCHER: User has an unknown activity state: " + activity.ActivityToken);
                        userState = PresenceStates.Busy;
                    }
                    break;
                case PresenceAvailability.DoNotDisturb:
                    userState = PresenceStates.DoNotDisturb;
                    break;
                case PresenceAvailability.IdleBusy:
                    userState = PresenceStates.IdleBusy;
                    break;
                case PresenceAvailability.IdleOnline:
                    userState = PresenceStates.IdleOnline;
                    break;
                case PresenceAvailability.Offline:
                    userState = PresenceStates.Offline;
                    break;
                case PresenceAvailability.Online:
                    userState = PresenceStates.Available;
                    break;
                case PresenceAvailability.None:
                default:
                    userState = PresenceStates.Unknown;
                    break;
            }
            return userState;
        }

        /// <summary>
        /// Event handler for notifying interested parties that a presence lookup has completed.
        /// </summary>
        public event EventHandler<PresenceObtainedEventArgs> GetPresenceCompleted;

        #endregion
    }
}
